import 'dart:async';

import 'package:flutter/material.dart';
import 'package:my_flutter_core/extensions/string_extension.dart';

/// Supports sync and async initial value for [TextFormField] since initialValue of [TextFormField] does NOT support async call.
///
/// [asyncInitialValue] takes [Future<String>] and
/// sets the value after async call is done by using [TextEditingController].
///
/// [initialValue] takes [String]. UI updates after re-build if [initialValue] changes.
class AsyncInitTextFormField extends StatefulWidget {
  const AsyncInitTextFormField({
    super.key,
    this.initialValue,
    this.asyncInitialValue,
    this.keyboardType,
    this.decoration,
    this.validator,
    this.onChanged,
    this.onSaved,
  });

  final String? initialValue;
  final Future<String?>? asyncInitialValue;
  final TextInputType? keyboardType;
  final InputDecoration? decoration;
  final FormFieldValidator<String>? validator;
  final ValueChanged<String>? onChanged;
  final FormFieldSetter<String>? onSaved;

  @override
  State<AsyncInitTextFormField> createState() => _AsyncInitTextFormFieldState();
}

class _AsyncInitTextFormFieldState extends State<AsyncInitTextFormField> {
  // For update [TextFormField] text after aysnc call is done.
  final TextEditingController _controller = TextEditingController();

  // It stores [widget.initialValue] for later value check.
  late String _initialValue = widget.initialValue ?? '';

  @override
  void initState() {
    super.initState();

    // Calls async method if needed.
    _asyncInitialValue();
  }

  @override
  void dispose() {
    _controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    setSyncInitialValue();

    return TextFormField(
      controller: _controller,
      keyboardType: widget.keyboardType,
      decoration: widget.decoration,
      validator: widget.validator,
      onChanged: widget.onChanged,
      onSaved: widget.onSaved,
    );
  }

  Future<void> _asyncInitialValue() async {
    final res = await widget.asyncInitialValue;

    if (res.isNotNullOrEmpty) {
      _controller.text = res!;
    }
  }

  /// Sets initialValue if it changes during build.
  void setSyncInitialValue() {
    if (widget.initialValue != null && widget.initialValue != _initialValue) {
      _initialValue = widget.initialValue!;

      // Timer works like setTimeout in js. It pushs the task into antoher queue and it will be executed after build is done.
      Timer(
        const Duration(milliseconds: 0),
        () => _controller.text = _initialValue,
      );
    }
  }
}
